

#ifndef UD_DBOBJPTR_H
#define UD_DBOBJPTR_H

#include <assert.h>
#include "zdbsymtb.h"

#ifdef ASSERT
#define DbObjPtr_Assert ASSERT
#elif defined assert
#define DbObjPtr_Assert assert
#elif defined _ASSERTE
#define DbObjPtr_Assert _ASSERTE
#else
#define DbObjPtr_Assert(T)
#endif

#pragma pack (push, 8)

template<class T_OBJECT>
class UcDbObjectPointerBase
{
public:
    UcDbObjectPointerBase();
    virtual ~UcDbObjectPointerBase();

    const T_OBJECT *  object() const;
    T_OBJECT *        object();

    const T_OBJECT *  operator->() const;
    T_OBJECT *        operator->();
                      operator const T_OBJECT*() const;
#if DBOBJPTR_EXPOSE_PTR_REF
                      operator T_OBJECT* &();
#else
                      operator T_OBJECT*();
#endif

    Ucad::ErrorStatus openStatus() const;

    Ucad::ErrorStatus open(UcDbObjectId   objId,
                           UcDb::OpenMode mode,
                           bool           openErased = false);

    Ucad::ErrorStatus acquire(T_OBJECT *& pObjToAcquire);
    Ucad::ErrorStatus release(T_OBJECT *& pReleasedObj);
    Ucad::ErrorStatus close();

    Ucad::ErrorStatus create();

protected:
    UcDbObjectPointerBase(UcDbObjectId   objId,
                          UcDb::OpenMode mode,
                          bool           openErased);

#if DBOBJPTR_EXPOSE_PTR_REF
    UcDbObjectPointerBase(T_OBJECT * pObject);
    void operator=(T_OBJECT *pObject);
#endif

    T_OBJECT *        m_ptr;
    Ucad::ErrorStatus m_status;

private:
    
    UcDbObjectPointerBase(UcDbObjectPointerBase & pObject);
    UcDbObjectPointerBase& operator=(UcDbObjectPointerBase & pObject);

    Ucad::ErrorStatus closeInternal();
};

template<class T_OBJECT>
class UcDbObjectPointer : public UcDbObjectPointerBase<T_OBJECT>
{
public:
    UcDbObjectPointer();
    UcDbObjectPointer(UcDbObjectId   objId,
                      UcDb::OpenMode mode,
                      bool           openErased = false);
    
#if DBOBJPTR_EXPOSE_PTR_REF
    UcDbObjectPointer(T_OBJECT * pObject);
    void operator=(T_OBJECT *pObject);
#endif

    Ucad::ErrorStatus open(UcDbObjectId objId,
                      UcDb::OpenMode mode,
                      bool           openErased = false);

private:
    
    UcDbObjectPointer(UcDbObjectPointer & pObject);
    UcDbObjectPointer& operator=(UcDbObjectPointer & pObject);
};

typedef UcDbObjectPointer<UcDbDictionary> UcDbDictionaryPointer;
typedef UcDbObjectPointer<UcDbEntity>     UcDbEntityPointer;

template<class T_OBJECT>
class UcDbSymbolTablePointer : public UcDbObjectPointerBase<T_OBJECT>
{
public:
    UcDbSymbolTablePointer();
    UcDbSymbolTablePointer(UcDbObjectId objId, UcDb::OpenMode mode);
    UcDbSymbolTablePointer(UcDbDatabase * pDb, UcDb::OpenMode mode);
#if DBOBJPTR_EXPOSE_PTR_REF
    UcDbSymbolTablePointer(T_OBJECT * pObject);
    void operator=(T_OBJECT *pObject);
#endif

    Ucad::ErrorStatus open(UcDbObjectId objId, UcDb::OpenMode mode);
    Ucad::ErrorStatus open(UcDbDatabase* pDb, UcDb::OpenMode mode);

private:
    
    UcDbSymbolTablePointer(UcDbSymbolTablePointer & pObject);
    UcDbSymbolTablePointer& operator=(UcDbSymbolTablePointer & pObject);

    typedef typename T_OBJECT::RecordType T2;
};

typedef UcDbSymbolTablePointer<UcDbBlockTable>     UcDbBlockTablePointer;
typedef UcDbSymbolTablePointer<UcDbDimStyleTable>  UcDbDimStyleTablePointer;
typedef UcDbSymbolTablePointer<UcDbLayerTable>     UcDbLayerTablePointer;
typedef UcDbSymbolTablePointer<UcDbLinetypeTable>  UcDbLinetypeTablePointer;
typedef UcDbSymbolTablePointer<UcDbRegAppTable>    UcDbRegAppTablePointer;
typedef UcDbSymbolTablePointer<UcDbTextStyleTable> UcDbTextStyleTablePointer;
typedef UcDbSymbolTablePointer<UcDbUCSTable>       UcDbUCSTablePointer;
typedef UcDbSymbolTablePointer<UcDbViewTable>      UcDbViewTablePointer;
typedef UcDbSymbolTablePointer<UcDbViewportTable>  UcDbViewportTablePointer;

template<class T_OBJECT>
class UcDbSymbolTableRecordPointer : public UcDbObjectPointerBase<T_OBJECT>
{
public:
    UcDbSymbolTableRecordPointer();
    UcDbSymbolTableRecordPointer(UcDbObjectId   objId,
                                 UcDb::OpenMode mode,
                                 bool           openErased = false);
    UcDbSymbolTableRecordPointer(const ZTCHAR *   name,
                                 UcDbDatabase * pDb,
                                 UcDb::OpenMode mode,
                                 bool           openErased = false);

#if DBOBJPTR_EXPOSE_PTR_REF
    UcDbSymbolTableRecordPointer(T_OBJECT * pObject);
    void operator=(T_OBJECT *pObject);
#endif

    Ucad::ErrorStatus open(UcDbObjectId   objId,
                           UcDb::OpenMode mode,
                           bool           openErased = false);
    Ucad::ErrorStatus open(const ZTCHAR *   name,
                           UcDbDatabase * pDb,
                           UcDb::OpenMode mode,
                           bool           openErased = false);

private:
    
    UcDbSymbolTableRecordPointer(UcDbSymbolTableRecordPointer & pObject);
    UcDbSymbolTableRecordPointer&
                       operator=(UcDbSymbolTableRecordPointer & pObject);

    typedef typename T_OBJECT::TableType T2;
};

typedef UcDbSymbolTableRecordPointer<UcDbBlockTableRecord>
                                     UcDbBlockTableRecordPointer;
typedef UcDbSymbolTableRecordPointer<UcDbDimStyleTableRecord>
                                     UcDbDimStyleTableRecordPointer;
typedef UcDbSymbolTableRecordPointer<UcDbLayerTableRecord>
                                     UcDbLayerTableRecordPointer;
typedef UcDbSymbolTableRecordPointer<UcDbLinetypeTableRecord>
                                     UcDbLinetypeTableRecordPointer;
typedef UcDbSymbolTableRecordPointer<UcDbRegAppTableRecord>
                                     UcDbRegAppTableRecordPointer;
typedef UcDbSymbolTableRecordPointer<UcDbTextStyleTableRecord>
                                     UcDbTextStyleTableRecordPointer;
typedef UcDbSymbolTableRecordPointer<UcDbUCSTableRecord>
                                     UcDbUCSTableRecordPointer;
typedef UcDbSymbolTableRecordPointer<UcDbViewTableRecord>
                                     UcDbViewTableRecordPointer;
typedef UcDbSymbolTableRecordPointer<UcDbViewportTableRecord>
                                     UcDbViewportTableRecordPointer;

template<class T_OBJECT> inline
UcDbObjectPointerBase<T_OBJECT>::UcDbObjectPointerBase()
    : m_ptr(NULL),
      m_status(Ucad::eNullObjectPointer)
{
}

template<class T_OBJECT> inline
UcDbObjectPointerBase<T_OBJECT>::UcDbObjectPointerBase(
    UcDbObjectId   objId,
    UcDb::OpenMode mode,
    bool           openErased)
    : m_ptr(NULL),
      m_status(ucdbOpenObject(m_ptr, objId, mode, openErased))
{
}

template<class T_OBJECT> inline
UcDbObjectPointerBase<T_OBJECT>::~UcDbObjectPointerBase()
{
    if (m_ptr != NULL) {
        assert(m_status == Ucad::eOk);
        Ucad::ErrorStatus closeStatus = closeInternal();
        assert(closeStatus == Ucad::eOk);
    }
}

template<class T_OBJECT> inline Ucad::ErrorStatus 
UcDbObjectPointerBase<T_OBJECT>::open(
    UcDbObjectId   objId,
    UcDb::OpenMode mode,
    bool           openErased)
{
    if (m_ptr != NULL) {
        assert(m_status == Ucad::eOk);
        Ucad::ErrorStatus closeStatus = closeInternal();
        if (closeStatus != Ucad::eOk)
            return closeStatus;
    }
    m_status = ucdbOpenObject(m_ptr, objId, mode, openErased);
    return m_status;
}

template<class T_OBJECT> inline const T_OBJECT *
UcDbObjectPointerBase<T_OBJECT>::object() const
{
    assert(m_status == Ucad::eOk);
    assert(m_ptr != NULL);
    DbObjPtr_Assert(m_ptr == NULL || m_ptr->isReadEnabled());
    return m_ptr;
}

template<class T_OBJECT> inline T_OBJECT *
UcDbObjectPointerBase<T_OBJECT>::object()
{
    assert(m_status == Ucad::eOk);
    assert(m_ptr != NULL);
    DbObjPtr_Assert(m_ptr == NULL || m_ptr->isReadEnabled());
    return m_ptr;
}

template<class T_OBJECT> inline const T_OBJECT *
UcDbObjectPointerBase<T_OBJECT>::operator->() const
{
    return object();
}

template<class T_OBJECT> inline T_OBJECT *
UcDbObjectPointerBase<T_OBJECT>::operator->()
{
    return object();
}

template<class T_OBJECT> inline 
UcDbObjectPointerBase<T_OBJECT>::operator const T_OBJECT*() const
{
    return object();
}

#if DBOBJPTR_EXPOSE_PTR_REF
template<class T_OBJECT> inline 
UcDbObjectPointerBase<T_OBJECT>::operator T_OBJECT* &()
{
    
    return this->m_ptr;
}
#else
template<class T_OBJECT> inline 
UcDbObjectPointerBase<T_OBJECT>::operator T_OBJECT*()
{
    return object();
}
#endif

template<class T_OBJECT> inline Ucad::ErrorStatus
UcDbObjectPointerBase<T_OBJECT>::openStatus() const
{
    return m_status;
}

template<class T_OBJECT> inline Ucad::ErrorStatus
UcDbObjectPointerBase<T_OBJECT>::acquire(T_OBJECT *& pObjToAcquire)
{
    if (pObjToAcquire == NULL)
        return Ucad::eNullObjectPointer;
    if (m_ptr != NULL) {
        assert(m_status == Ucad::eOk);
        Ucad::ErrorStatus closeStatus = closeInternal();
        if (closeStatus != Ucad::eOk)
            return closeStatus;
    }
    m_ptr = pObjToAcquire;
    m_status = Ucad::eOk;
    pObjToAcquire = NULL;
    return Ucad::eOk;
}

template<class T_OBJECT> inline Ucad::ErrorStatus
UcDbObjectPointerBase<T_OBJECT>::release(T_OBJECT *& pReleasedObj)
{
    if (m_ptr == NULL)
        return Ucad::eNullObjectPointer;
    assert(m_status == Ucad::eOk);
    pReleasedObj = m_ptr;
    m_ptr    = NULL;
    m_status = Ucad::eNullObjectPointer;
    return Ucad::eOk;
}

template<class T_OBJECT> inline Ucad::ErrorStatus
UcDbObjectPointerBase<T_OBJECT>::close()
{
    if (m_ptr == NULL)
        return Ucad::eNullObjectPointer;
    assert(m_status == Ucad::eOk);
    Ucad::ErrorStatus closeStatus = closeInternal();
    assert(closeStatus == Ucad::eOk);
    return Ucad::eOk;
}

template<class T_OBJECT> inline Ucad::ErrorStatus
UcDbObjectPointerBase<T_OBJECT>::create()
{
    T_OBJECT * pObject = new T_OBJECT;
    if (pObject == NULL)
        return Ucad::eNullObjectPointer;
    if (m_ptr != NULL) {
        assert(m_status == Ucad::eOk);
        Ucad::ErrorStatus closeStatus = closeInternal();
        if (closeStatus != Ucad::eOk) {
            delete pObject;
            return closeStatus;
        }
    }
    m_ptr = pObject;
    m_status = Ucad::eOk;
    return Ucad::eOk;
}

template<class T_OBJECT> inline Ucad::ErrorStatus
UcDbObjectPointerBase<T_OBJECT>::closeInternal()
{
    if (m_ptr == NULL)
        return Ucad::eOk;
    Ucad::ErrorStatus es = Ucad::eOk;
    if (m_ptr->objectId().isNull()) {
        delete m_ptr;
        es = Ucad::eOk;
    } else {
        es = m_ptr->close();
    }
    m_ptr       = NULL;
    m_status    = Ucad::eNullObjectPointer;
    return es;
}

template<class T_OBJECT> inline
UcDbObjectPointer<T_OBJECT>::UcDbObjectPointer()
    : UcDbObjectPointerBase<T_OBJECT>()
{
}

template<class T_OBJECT> inline
UcDbObjectPointer<T_OBJECT>::UcDbObjectPointer(
    UcDbObjectId   objId,
    UcDb::OpenMode mode,
    bool           openErased)
    : UcDbObjectPointerBase<T_OBJECT>(objId, mode, openErased)
{
}

template<class T_OBJECT> inline Ucad::ErrorStatus
UcDbObjectPointer<T_OBJECT>::open(
    UcDbObjectId   objId,
    UcDb::OpenMode mode,
    bool           openErased)
{
    return UcDbObjectPointerBase<T_OBJECT>::open(objId, mode, openErased);
}

template<class T_OBJECT> inline
UcDbSymbolTablePointer<T_OBJECT>::UcDbSymbolTablePointer()
    : UcDbObjectPointerBase<T_OBJECT>()
{
}

template<class T_OBJECT> inline
UcDbSymbolTablePointer<T_OBJECT>::UcDbSymbolTablePointer(
    UcDbObjectId   objId,
    UcDb::OpenMode mode)
    : UcDbObjectPointerBase<T_OBJECT>(objId, mode, false)
{
}

template<class T_OBJECT> inline
UcDbSymbolTablePointer<T_OBJECT>::UcDbSymbolTablePointer(
    UcDbDatabase * pDb,
    UcDb::OpenMode mode)
    : UcDbObjectPointerBase<T_OBJECT>()
{
  this->m_status = (pDb == NULL) ? Ucad::eNullObjectPointer
                             : pDb->getSymbolTable(this->m_ptr, mode);
}

template<class T_OBJECT> inline Ucad::ErrorStatus
UcDbSymbolTablePointer<T_OBJECT>::open(
    UcDbObjectId   objId,
    UcDb::OpenMode mode)
{
    return UcDbObjectPointerBase<T_OBJECT>::open(objId, mode, false);
}

template<class T_OBJECT> inline Ucad::ErrorStatus
UcDbSymbolTablePointer<T_OBJECT>::open(
    UcDbDatabase* pDb,
    UcDb::OpenMode mode)
{
    if (pDb == NULL)
        return Ucad::eInvalidInput;
    Ucad::ErrorStatus es = Ucad::eOk;
    if (this->m_ptr != NULL)
        es = this->close();
    if (es == Ucad::eOk) {
        es = pDb->getSymbolTable(this->m_ptr, mode);
        this->m_status = es;
    }
    return es;
}

template<class T_OBJECT> inline
UcDbSymbolTableRecordPointer<T_OBJECT>::UcDbSymbolTableRecordPointer()
    : UcDbObjectPointerBase<T_OBJECT>()
{
}

template<class T_OBJECT> inline
UcDbSymbolTableRecordPointer<T_OBJECT>::UcDbSymbolTableRecordPointer(
    UcDbObjectId   objId,
    UcDb::OpenMode mode,
    bool           openErased)
    : UcDbObjectPointerBase<T_OBJECT>(objId, mode, openErased)
{
}

template<class T_OBJECT> inline
UcDbSymbolTableRecordPointer<T_OBJECT>
    ::UcDbSymbolTableRecordPointer(
    const ZTCHAR *   name,
    UcDbDatabase * pDb,
    UcDb::OpenMode mode,
    bool           openErased)
    : UcDbObjectPointerBase<T_OBJECT>()
{
    if (name == NULL)
        this->m_status = Ucad::eInvalidInput;
    else {
        UcDbSymbolTablePointer<typename T_OBJECT::TableType>
            pTable(pDb, UcDb::kForRead);
        this->m_status = pTable.openStatus();
        if (this->m_status == Ucad::eOk)
           this->m_status = pTable->getAt(name, this->m_ptr, mode, openErased);
    }
}

template<class T_OBJECT> inline Ucad::ErrorStatus
UcDbSymbolTableRecordPointer<T_OBJECT>::open(
    UcDbObjectId   objId,
    UcDb::OpenMode mode,
    bool           openErased)
{
    return UcDbObjectPointerBase<T_OBJECT>::open(objId, mode, openErased);
}

template<class T_OBJECT> inline Ucad::ErrorStatus
UcDbSymbolTableRecordPointer<T_OBJECT>
    ::open(const ZTCHAR *   name,
           UcDbDatabase * pDb,
           UcDb::OpenMode mode,
           bool           openErased)
{
    if (name == NULL)
        return Ucad::eInvalidInput;
    else {
        UcDbSymbolTablePointer<typename T_OBJECT::TableType>
            pTable(pDb, UcDb::kForRead);
        Ucad::ErrorStatus es = pTable.openStatus();
        if (es == Ucad::eOk) {
            if (this->m_ptr != NULL)
                es = this->close();
            if (es == Ucad::eOk) {
                es = pTable->getAt(name, this->m_ptr, mode, openErased);
                this->m_status = es;
            }
        }
        return es;
    }
}

#if DBOBJPTR_EXPOSE_PTR_REF

template<class T_OBJECT> inline
UcDbObjectPointerBase<T_OBJECT>::UcDbObjectPointerBase(T_OBJECT *pObject)
    : m_ptr(NULL),
      m_status(Ucad::eNullObjectPointer)
{
    if (pObject != NULL)
        this->acquire(pObject);
}

template<class T_OBJECT> inline void
UcDbObjectPointerBase<T_OBJECT>::operator=(T_OBJECT *pObject)
{
    if (pObject == NULL) {
        DbObjPtr_Assert(this->m_ptr == NULL);
    }
    else
        this->acquire(pObject);
}

template<class T_OBJECT> inline
UcDbObjectPointer<T_OBJECT>::UcDbObjectPointer(T_OBJECT *pObject)
                : UcDbObjectPointerBase<T_OBJECT>(pObject)
{
}

template<class T_OBJECT> inline UcDbSymbolTablePointer<T_OBJECT>::
        UcDbSymbolTablePointer(T_OBJECT *pObject)
                : UcDbObjectPointerBase<T_OBJECT>(pObject)
{
}

template<class T_OBJECT> inline UcDbSymbolTableRecordPointer<T_OBJECT>::
        UcDbSymbolTableRecordPointer(T_OBJECT *pObject)
                : UcDbObjectPointerBase<T_OBJECT>(pObject)
{
}

template<class T_OBJECT> inline void
UcDbObjectPointer<T_OBJECT>::operator=(T_OBJECT *pObject)
{
    UcDbObjectPointerBase<T_OBJECT>::operator =(pObject);
}

template<class T_OBJECT> inline void
UcDbSymbolTablePointer<T_OBJECT>::operator=(T_OBJECT *pObject)
{
    UcDbObjectPointerBase<T_OBJECT>::operator =(pObject);
}

template<class T_OBJECT> inline void
UcDbSymbolTableRecordPointer<T_OBJECT>::operator=(T_OBJECT *pObject)
{
    UcDbObjectPointerBase<T_OBJECT>::operator =(pObject);
}

#endif  

#pragma pack (pop)

#endif 
